/*
 *  Copyright 2009 Advanced Industrial Science and Technology (AIST).
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/*! \file
 *  \brief
 */
#pragma once

#include <thrust/pending/property.hpp>
#include <thrust/property_map/property_map.hpp>

namespace thrust {
  
  enum default_color_type { 
    white_color
    , gray_color
    , green_color
    , red_color
    , black_color
  };
  
  template <class ColorValue>
  struct color_traits {
    static default_color_type white() { return white_color;}
    static default_color_type gray()  { return gray_color;}
    static default_color_type green() { return green_color;}
    static default_color_type red()   { return red_color;}
    static default_color_type black() { return black_color;}
  };

  struct graph_property_tag {};
  struct vertex_property_tag {};
  struct edge_property_tag {};

  
  // property_kind is defined in <thrust/pending/property.hpp>
#define THRUST_INSTALL_PROPERTY( KIND, NAME) \
  template <>                                \
  struct property_kind<KIND##_##NAME##_t> {  \
    typedef KIND##_property_tag type;        \
  };
  
#define THRUST_DEF_PROPERTY( KIND, NAME)   \
  enum KIND##_##NAME##_t { KIND##_##NAME}; \
  THRUST_INSTALL_PROPERTY( KIND, NAME);
  
  THRUST_DEF_PROPERTY( vertex, all);
  THRUST_DEF_PROPERTY( edge, all);
  THRUST_DEF_PROPERTY( graph, all);

  THRUST_DEF_PROPERTY( vertex, index);
  THRUST_DEF_PROPERTY( vertex, index1);
  THRUST_DEF_PROPERTY( vertex, index2);
  THRUST_DEF_PROPERTY( vertex, name);
  THRUST_DEF_PROPERTY( vertex, root);
  THRUST_DEF_PROPERTY( vertex, distance);
  THRUST_DEF_PROPERTY( vertex, distance2);
  THRUST_DEF_PROPERTY( vertex, color);
  THRUST_DEF_PROPERTY( vertex, degree);
  THRUST_DEF_PROPERTY( vertex, in_degree);
  THRUST_DEF_PROPERTY( vertex, out_degree);
  THRUST_DEF_PROPERTY( vertex, current_degree);
  THRUST_DEF_PROPERTY( vertex, priority);
  THRUST_DEF_PROPERTY( vertex, discover_time);
  THRUST_DEF_PROPERTY( vertex, finish_time);
  THRUST_DEF_PROPERTY( vertex, predecessor);
  THRUST_DEF_PROPERTY( vertex, rank);
  THRUST_DEF_PROPERTY( vertex, centrality);
  THRUST_DEF_PROPERTY( vertex, lowpint);
  THRUST_DEF_PROPERTY( vertex, potential);
  THRUST_DEF_PROPERTY( vertex, update);
  
  THRUST_DEF_PROPERTY( edge, index);
  THRUST_DEF_PROPERTY( edge, name);
  THRUST_DEF_PROPERTY( edge, weight);
  THRUST_DEF_PROPERTY( edge, weight2);
  THRUST_DEF_PROPERTY( edge, color);
  THRUST_DEF_PROPERTY( edge, reverse);
  THRUST_DEF_PROPERTY( edge, capacity);
  THRUST_DEF_PROPERTY( edge, flow);
  THRUST_DEF_PROPERTY( edge, residual_capacity);
  THRUST_DEF_PROPERTY( edge, centrality);
  THRUST_DEF_PROPERTY( edge, discover_time);
  THRUST_DEF_PROPERTY( edge, update);
  THRUST_DEF_PROPERTY( edge, finished);

  THRUST_DEF_PROPERTY( graph, name);
  THRUST_DEF_PROPERTY( graph, visitor);

#undef THRUST_DEF_PROPERTY

  /*namespace detail {
    struct dummy_vertex_property_selector {
      template <class >
    };
    }*/ // namespace detail

  namespace detail {

    // dummy vertex and edge property selector
    struct dummy_vertex_property_selector {
      struct bind_ {
	typedef identity_property_map type;
	typedef identity_property_map const_type;
      };
    };
    struct dummy_edge_property_selector {
      struct bind_ {
	typedef identity_property_map type;
	typedef identity_property_map const_type;
      };
    };
  } // namespace detail
  template <class GraphTag>
  struct vertex_property_selector {
    typedef detail::dummy_vertex_property_selector type;
  };
  template <class GraphTag>
  struct edge_property_selector {
    typedef detail::dummy_edge_property_selector type;
  };
  
  namespace detail {

    template <typename A>
    struct return_void { 
      typedef void type;
    };
    template <typename Graph, typename Enable =void>
    struct graph_tag_or_void {
      typedef void type;
    };
    template <typename Graph>
    struct graph_tag_or_void<Graph,typename return_void<typename Graph::graph_tag>::type> {
      typedef typename Graph::graph_tag type;
    };
    
    template <class Graph, class PropertyTag>
    struct vertex_property_map {
      typedef typename thrust::vertex_property_type<Graph>::type Property;
      typedef typename graph_tag_or_void<Graph>::type graph_tag;
      typedef typename vertex_property_selector<graph_tag>::type Selector;
      typedef typename Selector::template bind_<Graph,Property,PropertyTag> Bind;
    public:
      typedef typename Bind::type       type;
      typedef typename Bind::const_type const_type;
    };
    
    template <class Graph, class PropertyTag>
    struct edge_property_map {
      typedef typename edge_property_type<Graph>::type Property;
      typedef typename graph_tag_or_void<Graph>::type graph_tag;
      typedef typename edge_property_selector<graph_tag>::type Selector;
      typedef typename Selector::template bind_<Graph,Property,PropertyTag> Bind;
    public:
      typedef typename Bind::type       type;
      typedef typename Bind::const_type const_type;
    };
    
    // This selects the kind of property map, whether is maps from 
    // edges or from vertices.
    //
    // It is overlay complicated because it's a workaround for 
    // partial specialization.
    struct choose_vertex_property_map {
      template <class Graph, class Property>
      struct bind_ {
        typedef vertex_property_map<Graph,Property> type;
      };
    };
    struct choose_edge_property_map {
      template <class Graph, class Property>
      struct bind_ {
	typedef edge_property_map<Graph,Property> type;
      };
    };

    template <class Kind>
    struct property_map_kind_selector
    {
      typedef choose_vertex_property_map type;
    };
    template <>
    struct property_map_kind_selector<vertex_property_tag>
    {
      typedef choose_vertex_property_map type;
    };
    template <>
    struct property_map_kind_selector<edge_property_tag>
    {
      typedef choose_edge_property_map type;
    };
  } // namespace detail

  template <class Graph, class Property>
  struct property_map
  {
  private:
    typedef typename property_kind<Property>::type Kind;
    typedef typename detail::property_map_kind_selector<Kind>::type Selector;
    typedef typename Selector::template bind_<Graph,Property> Bind;
    typedef typename Bind::type Map;
  public:
    typedef typename Map::type type;
    typedef typename Map::const_type const_type;
  };
  
  //===================================
  // vertex, edge and graph properties
  /*template <class Graph>
  class vertex_property {
  public:
    typedef typename Graph::vertex_property_type type;
  };
  
  template <class Graph>
  class edge_property {
  public:
    typedef typename Graph::edge_property_type type;
  };

  template <class Graph, class Property>
  class graph_property {
  public:
    typedef typename property_value<
      typename Graph::graph_property_type
      , Property
      >::type type;
      };*/
  
} // namespace thrust
